/*
* Copyright (c) 2018-2022 Allwinner Technology Co. Ltd. All rights reserved.
* Author: BT Team
* Date: 2022.03.12
* Description: gatt-server test demo.
*/

#include <bt_manager.h>
#include <errno.h>
#include <fcntl.h>
#include <fcntl.h>
#include <getopt.h>
#include <poll.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/signalfd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

#include "bt_dev_list.h"
#include "bt_hal.h"
#include "bt_log.h"

#define SERVICE_HANDLE_START 11

static uint16_t service_handle;
static uint16_t service_handle_char;
static uint16_t service_handle_desc;
static uint8_t server_is_connected = 0;
static int is_gatt_testcase_server = 0;
char device_name[28] = "aw-ble-test-007";

static btmg_adv_data_t le_adv_data = {0};
static btmg_scan_rsp_data_t rsp_data = {0};
static uint8_t le_adv_status = 0;
static btmg_adv_data_t nonconn_le_adv_data = {0};
static btmg_scan_rsp_data_t nonconn_rsp_data = {0};
static uint8_t nonconn_le_adv_status = 0;
static int char_handle = 0;
static int dev_name_char_handle = 0;
static int notify_handle = 0;
static int indicate_handle = 0;

static ms_hal_ble_attrib_callback_t server_callback[64] = {NULL};

static void bt_test_gatt_connection_cb(char *addr,
                                       gatts_connection_event_t event,
                                       int err)
{
    if (event == BT_GATT_CONNECTION)
    {
        printf("gatt server Connected: %s.\n", addr);
        ms_hal_ble_stack_msg_t msg;
        bzero(&msg, sizeof(msg));
        msg.event_type = MS_HAL_BLE_STACK_EVENT_CONNECTION_REPORT;
        msg.param.conn_hdl = SERVER_HANDLE_ID;
        msg.param.connection_msg.addr;
        msg.param.connection_msg.role = 1;
        server_is_connected = 1;
        ble_gatt_data_t device = {0};
        if (strlen(addr) == 17 && addr[2] == ':')
        {
            for (int i = 0; i < 6; i++)
            {
                char buf[3] = {0};
                memcpy(buf, &addr[i * 3], 2);
                device.addr[i] = strtoull(buf, NULL, 16);
            }
        }
        memcpy(msg.param.connection_msg.addr, device.addr, 6);
        if (ms_hal_ble_stack_callback)
            ms_hal_ble_stack_callback(msg);
        device.conn_id = SERVER_HANDLE_ID;
        device.is_slave = 1;
        add_device_to_list(device);
    }
    else if (event == BT_GATT_DISCONNECT)
    {
        printf("gatt server Disconnected: %s (reason UNKONW)\n", addr);
        server_is_connected = 0;
        del_device_to_list(SERVER_HANDLE_ID);
        ms_hal_ble_stack_msg_t msg;
        bzero(&msg, sizeof(msg));
        msg.event_type = MS_HAL_BLE_STACK_EVENT_DISCONNECTED;
        msg.param.conn_hdl = SERVER_HANDLE_ID;
        if (ms_hal_ble_stack_callback)
            ms_hal_ble_stack_callback(msg);
    }
    else
    {
        printf("gatt server event unkown.\n");
    }
}

static void bt_test_gatt_add_service_cb(gatts_add_svc_msg_t *msg)
{
    if (msg != NULL)
    {
        service_handle = msg->svc_handle;
        printf("add service handle: %d, handle max number: %d\n", service_handle,
               msg->handle_num);
    }
}

static void bt_test_gatts_add_char_cb(gatts_add_char_msg_t *msg)
{
    if (msg != NULL)
    {
        service_handle_char = msg->char_handle;
        printf("add char,uuid: %s,chr handle is %d\n", msg->uuid, msg->char_handle);
    }
}

static void bt_test_gatt_add_desc_cb(gatts_add_desc_msg_t *msg)
{
    if (msg != NULL)
    {
        service_handle_desc = msg->desc_handle;
        printf("desc handle is %d\n", msg->desc_handle);
    }
}

static void bt_test_gatt_char_read_request_cb(gatts_char_read_req_t *chr_read)
{
    char value[1];
    static unsigned char count = 0;
    char dev_name[] = "aw_ble_test_1149";

    printf("trans_id:%d,attr_handle:%d,offset:%d\n", chr_read->trans_id,
           chr_read->attr_handle, chr_read->offset);
    // for (int k = 0; k < sizeof(server_callback) / sizeof(ms_hal_ble_attrib_callback_t); k++)
    // {
    //     if (server_callback[k].read_cb)
    //     {
    //         server_callback[k].read_cb(char_write->value, char_write->value_len);
    //     }
    // }
}

static void
bt_test_gatts_send_indication_cb(gatts_send_indication_t *send_ind)
{
    ms_hal_ble_stack_msg_t msg;
    bzero(&msg, sizeof(msg));
    msg.event_type = MS_HAL_BLE_STACK_EVENT_CMP_INDICATE;
    msg.param.conn_hdl = SERVER_HANDLE_ID;
    if (ms_hal_ble_stack_callback)
        ms_hal_ble_stack_callback(msg);
}

static void bt_test_gatt_char_write_request_cb(gatts_char_write_req_t *char_write)
{
    static unsigned char data_value[1] = {0};
    int ret = 0;

    if (char_write)
    {
        printf("\033[1;34m attr_handle: %d,tran_id: %d \033[0m\n", char_write->attr_handle,
               char_write->trans_id);
        for(int i =0; i < char_write->value_len; i++){
            printf("\033[1;34m %x ", char_write->value[i]);
        }
        printf("\033[0m\n");
        for (int k = 0; k < sizeof(server_callback) / sizeof(ms_hal_ble_attrib_callback_t); k++)
        {
            if (server_callback[k].write_cb)
            {
                // char value[64] = {0};
                // if(char_write->value_len == 2){
                //     value[0] = char_write->value[1];
                //     value[1] = char_write->value[0];
                //     server_callback[k].write_cb(value, char_write->value_len);
                // }else
                    server_callback[k].write_cb(char_write->value, char_write->value_len);
            }
        }
    }

    printf("char_write->need_rsp: %d\n", char_write->need_rsp);
    if (char_write->need_rsp)
    {
        gatts_write_rsp_t data;
        data.trans_id = char_write->trans_id;
        data.attr_handle = char_write->attr_handle;
        data.state = BT_GATT_SUCCESS;
        ret = bt_manager_gatt_server_send_write_response(&data);
        if (ret != 0)
            printf("send write response failed!\n");
        else
            printf("send write response success!\n");
    }
}

ms_hal_result_t ms_hal_ble_stack_stop_service_clean(void)
{
    bzero(server_callback, sizeof(server_callback));
}

static void
bt_test_gatt_desc_read_requset_cb(gatts_desc_read_req_t *desc_read)
{
    char value[1];
    static unsigned char count = 0;

    printf("trans_id:%d,attr_handle:%d,offset:%d\n", desc_read->trans_id,
           desc_read->attr_handle, desc_read->offset);

    if (desc_read)
    {
        gatts_send_read_rsp_t data;
        data.trans_id = desc_read->trans_id;
        data.attr_handle = desc_read->attr_handle;
        data.status = 0x0b;
        data.auth_req = 0x00;
        value[0] = count;
        data.value = value;
        data.value_len = 1;
        bt_manager_gatt_server_send_read_response(&data);
        count++;
    }
}

static void
bt_test_gatt_desc_write_request_cb(gatts_desc_write_req_t *desc_write)
{
    int ret = 0;

    printf("enter\n");
    printf("desc_write->need_rsp: %d\n", desc_write->need_rsp);
    printf("desc_write->attr_handle: %d\n", desc_write->attr_handle);

    if (desc_write->need_rsp)
    {
        gatts_write_rsp_t data;
        data.trans_id = desc_write->trans_id;
        data.attr_handle = desc_write->attr_handle;
        data.state = BT_GATT_SUCCESS;
        ret = bt_manager_gatt_server_send_write_response(&data);
        if (ret != 0)
            printf("send write response failed!\n");
        else
            printf("send write response success!\n");
    }
}

static void set_adv_param(void)
{
    btmg_le_advertising_parameters_t adv_params;

    adv_params.min_interval = 0x0020;
    adv_params.max_interval = 0x01E0; /*range from 0x0020 to 0x4000*/
    adv_params.own_addr_type = BTMG_LE_RANDOM_ADDRESS;
    adv_params.adv_type = BTMG_LE_ADV_IND; /*ADV_IND*/
    adv_params.chan_map =
        BTMG_LE_ADV_CHANNEL_ALL; /*0x07, *bit0:channel 37, bit1:
                                  channel 38, bit2: channel39*/
    adv_params.filter = BTMG_LE_PROCESS_ALL_REQ;
    bt_manager_le_set_adv_param(&adv_params);
}

static int le_set_adv_data(const char *ble_name, uint16_t uuid)
{
    int dd;
    uint8_t manuf_len;
    int index;
    char advdata[31] = {0};
    char uuid_buf[5] = {0};
    int advdata_len = 0;

    index = 0;

    advdata[index] = 0x02;     /* flag len */
    advdata[index + 1] = 0x01; /* type for flag */
    advdata[index + 2] = 0x1A; // 0x05

    index += advdata[index] + 1;

    advdata[index] = strlen(ble_name) + 1; /* name len */
    advdata[index + 1] = 0x09;             /* type for local name */
    int name_len;
    name_len = strlen(ble_name);
    strcpy(&(advdata[index + 2]), ble_name);
    index += advdata[index] + 1;

    advdata[index] = 0x03;     /* uuid len */
    advdata[index + 1] = 0x03; /* type for complete list of 16-bit uuid */
    advdata[index + 2] = (char)(uuid & 0xFF);
    advdata[index + 3] = (char)((uuid >> 8) & 0xFF);
    index += advdata[index] + 1;

    btmg_adv_data_t adv;
    adv.data_len = index;
    memcpy(adv.data, advdata, 31);

    BTMG_INFO("ble name = [%s]", ble_name);
    return bt_manager_le_set_adv_data(&adv);
}

void bt_gatt_server_register_callback(btmg_callback_t *cb)
{
    cb->btmg_gatt_server_cb.gatts_add_svc_cb = bt_test_gatt_add_service_cb;
    cb->btmg_gatt_server_cb.gatts_add_char_cb = bt_test_gatts_add_char_cb;
    cb->btmg_gatt_server_cb.gatts_add_desc_cb = bt_test_gatt_add_desc_cb;
    cb->btmg_gatt_server_cb.gatts_connection_event_cb = bt_test_gatt_connection_cb;
    cb->btmg_gatt_server_cb.gatts_char_read_req_cb = bt_test_gatt_char_read_request_cb;
    cb->btmg_gatt_server_cb.gatts_char_write_req_cb = bt_test_gatt_char_write_request_cb;
    cb->btmg_gatt_server_cb.gatts_desc_read_req_cb = bt_test_gatt_desc_read_requset_cb;
    cb->btmg_gatt_server_cb.gatts_desc_write_req_cb = bt_test_gatt_desc_write_request_cb;
    cb->btmg_gatt_server_cb.gatts_send_indication_cb = bt_test_gatts_send_indication_cb;
}

ms_hal_result_t ble_gatt_service_add(ms_hal_ble_service_t *service)
{
    if (!service || service->attr_count <= 0)
    {
        return MS_HAL_RESULT_ERROR;
    }
    for (int i = 0; i < service->attr_count; i++)
    {
        printf("\033[1;34m chenql%d %d\033[0m\n", __LINE__,service->attrs[i].att_type);
        switch (service->attrs[i].att_type)
        {
        case ENUM_MS_HAL_BLE_ATTRIB_TYPE_SERVICE:
        {
            gatts_add_svc_t svc;
            bzero(&svc, sizeof(gatts_add_svc_t));
            svc.number = service->attr_count + SERVICE_HANDLE_START;
            char uuid[128] = {0};
            for (int j = 0; j < service->attrs[i].service_para.uuid_size; j++)
            {
                char data[16] = {0};
                if( j == 4 || j == 6 || j == 8 || j == 10){
                    strcat(uuid, "-");
                }
                sprintf(data, "%02x",service->attrs[i].service_para.uuid[service->attrs[i].service_para.uuid_size-1-j]);
                strcat(uuid, data);
            }
            svc.uuid = uuid;
            svc.primary = true;
            bt_manager_gatt_server_create_service(&svc);
            service->attrs[i].handle = service_handle;
        }
        break;
        case ENUM_MS_HAL_BLE_ATTRIB_TYPE_CHAR:
        {
            gatts_add_char_t chr1;
            bzero(&chr1, sizeof(gatts_add_char_t));
            chr1.permissions = service->attrs[i].charact_para.permission;
            chr1.properties = service->attrs[i].charact_para.property;
            int k = 0;
            for (; k < sizeof(server_callback) / sizeof(ms_hal_ble_attrib_callback_t); k++)
            {
                if (server_callback[k].null_cb == NULL &&
                    server_callback[k].read_cb == NULL &&
                    server_callback[k].write_cb == NULL)
                {
                    break;
                }
            }
            server_callback[k] = service->attrs[i].charact_para.callback;
            chr1.svc_handle = service_handle;
            char uuid[128] = {0};
            for (int j = 0; j < service->attrs[i].charact_para.uuid_size; j++)
            {
                char data[16] = {0};
                if( j == 4 || j == 6 || j == 8 || j == 10){
                    strcat(uuid, "-");
                }
                sprintf(data, "%02x",service->attrs[i].charact_para.uuid[service->attrs[i].charact_para.uuid_size-1-j]);
                strcat(uuid, data);
            }
            chr1.uuid = uuid;
            bt_manager_gatt_server_add_characteristic(&chr1);
             service->attrs[i].handle = service_handle_char;
        }
        break;
        case ENUM_MS_HAL_BLE_ATTRIB_TYPE_CHAR_USER_DESCR:
        case ENUM_MS_HAL_BLE_ATTRIB_TYPE_CHAR_CLIENT_CONFIG:
        {
            gatts_add_desc_t desc1;
            bzero(&desc1, sizeof(gatts_add_desc_t));
            desc1.permissions = service->attrs[i].user_desc_para.permission;
            char uuid[5] = {0};
            sprintf(uuid, "%x", service->attrs[i].att_type);
            desc1.uuid = uuid;
            desc1.svc_handle = service_handle;
            bt_manager_gatt_server_add_descriptor(&desc1);
            service->attrs[i].handle = service_handle_desc;
        }
        break;
        }
    }
    gatts_star_svc_t start_svc;
    bt_manager_gatt_server_start_service(&start_svc);
    return MS_HAL_RESULT_SUCCESS;
}

ms_hal_result_t ble_gatt_data_send(uint16_t conn_hdl,
                                   ms_hal_ble_gatts_attr_handle_t *handle,
                                   uint8_t *data, uint16_t len)
{
    printf("\033[1;34m chenql%d %d \033[0m\n", __LINE__,*handle);
    gatts_indication_data_t pData = {0};
    pData.attr_handle = *handle;
    pData.value = data;
    pData.value_len = len;
    printf("\033[1;34m chenql%d %d %d %d \033[0m\n", __LINE__,*handle,data,len);
    return bt_manager_gatt_server_send_indication(&pData);
}

ms_hal_result_t set_ble_name(char *device_name, int len)
{
    char ble_device_name[4096] = {0};
    memcpy(ble_device_name, device_name, len);
    if (nonconn_le_adv_status)
        bt_manager_le_enable_adv(false);
    ms_hal_result_t ret = le_set_adv_data(ble_device_name, 0);
    if (ret != 0)
    {
        ret = MS_HAL_RESULT_ERROR;
    }
    if (nonconn_le_adv_status)
        bt_manager_le_enable_adv(true);
    return ret;
}

uint8_t ble_adv_status(uint16_t handle)
{
    return le_adv_status;
    ;
}
uint8_t nonconn_ble_adv_status(uint16_t handle)
{
    return nonconn_le_adv_status;
    ;
}

ms_hal_result_t ble_legacy_adv_stop(uint16_t handle)
{
    if (handle == LE_ADV_HANDLE)
    {
        if (bt_manager_le_enable_adv(false) == 0)
        {
            le_adv_status = 0;
            return MS_HAL_RESULT_SUCCESS;
        }
        else
        {
            return MS_HAL_RESULT_ERROR;
        }
    }
    else if (handle == NONNECT_LE_ADV_HANDLE)
    {
        if (bt_manager_le_enable_adv(false) == 0)
        {
            nonconn_le_adv_status = 0;
            return MS_HAL_RESULT_SUCCESS;
        }
        else
        {
            return MS_HAL_RESULT_ERROR;
        }
    }
    else
    {
        return MS_HAL_RESULT_ERROR;
    }
}

ms_hal_result_t nonconn_ble_legacy_adv_stop(uint16_t handle)
{
    if (handle == LE_ADV_HANDLE)
    {
        if (bt_manager_le_enable_adv(false) == 0)
        {
            le_adv_status = 0;
            return MS_HAL_RESULT_SUCCESS;
        }
        else
        {
            return MS_HAL_RESULT_ERROR;
        }
    }
    else if (handle == NONNECT_LE_ADV_HANDLE)
    {
        if (bt_manager_le_enable_adv(false) == 0)
        {
            nonconn_le_adv_status = 0;
            return MS_HAL_RESULT_SUCCESS;
        }
        else
        {
            return MS_HAL_RESULT_ERROR;
        }
    }
    else
    {
        return MS_HAL_RESULT_ERROR;
    }
}

uint16_t ble_legacy_adv_start(ms_hal_ble_gap_adv_params *param,
                              ms_hal_ble_gap_adv_data *adv_data,
                              uint8_t enable)
{
    if (nonconn_le_adv_status)
        bt_manager_le_enable_adv(false);
    if (param)
    {
        btmg_le_advertising_parameters_t adv_params;
        bzero(&adv_params, sizeof(adv_params));
        adv_params.min_interval = param->adv_intv_min;
        adv_params.max_interval = param->adv_intv_max;
        /*range from 0x0020 to 0x4000*/
        adv_params.filter = BTMG_LE_PROCESS_ALL_REQ;
        switch (param->adv_type)
        {
        case MS_HAL_BLE_ADV_TPYE_IND:
            adv_params.adv_type = BTMG_LE_ADV_IND;
            break;
        case MS_HAL_BLE_ADV_TPYE_DIRECT_IND:
            adv_params.adv_type = BTMG_LE_ADV_DIRECT_LOW_IND;
            break;
        case MS_HAL_BLE_ADV_TPYE_NONCONN_IND:
            adv_params.adv_type = BTMG_LE_ADV_NONCONN_IND;
            break;
        case MS_HAL_BLE_ADV_TPYE_SCAN_IND:
            adv_params.adv_type = BTMG_LE_ADV_SCAN_IND;
            break;
        case MS_HAL_BLE_ADV_TPYE_EXT_IND:
        case MS_HAL_BLE_AUX_TPYE_ADV_IND:
        case MS_HAL_BLE_AUX_TPYE_SYNC_IND:
        case MS_HAL_BLE_AUX_TPYE_CHAIN_IND:
            adv_params.adv_type = BTMG_LE_ADV_IND;
            break;
        }
        adv_params.own_addr_type = BTMG_LE_RANDOM_ADDRESS;
        adv_params.chan_map = BTMG_LE_ADV_CHANNEL_ALL; /*0x07, *bit0:channel 37,
                                                      bit1: channel 38, bit2:
                                                      channel39*/
        bt_manager_le_set_adv_param(&adv_params);
        bt_manager_le_set_random_address();
    }
    if (adv_data->adv_len > 0 && adv_data->adv_data)
    {
        bzero(&le_adv_data, sizeof(le_adv_data));
        memcpy(le_adv_data.data, adv_data->adv_data, adv_data->adv_len);
        le_adv_data.data_len = adv_data->adv_len;
        bt_manager_le_set_adv_data(&le_adv_data);
    }
    if (adv_data->rsp_len > 0 && adv_data->rsp_data)
    {
        bzero(&rsp_data, sizeof(rsp_data));
        memcpy(rsp_data.data, adv_data->rsp_data, adv_data->rsp_len);
        rsp_data.data_len = adv_data->rsp_len;
        bt_manager_le_set_scan_rsp_data(&rsp_data);
    }
    if (enable)
    {
        if (bt_manager_le_enable_adv(true) == 0)
        {
            le_adv_status = 1;
        }
        else
        {
            return -1;
        }
    }
    return LE_ADV_HANDLE;
}

uint16_t nonconn_ble_legacy_adv_start(ms_hal_ble_gap_adv_params *param, ms_hal_ble_gap_adv_data *adv_data,
                                      uint8_t enable)
{
    if (nonconn_le_adv_status)
        bt_manager_le_enable_adv(false);
    btmg_le_advertising_parameters_t adv_params;
    bzero(&adv_params, sizeof(adv_params));
    adv_params.min_interval = param->adv_intv_min;
    adv_params.max_interval = param->adv_intv_max; /*range from 0x0020 to 0x4000*/
    adv_params.adv_type = BTMG_LE_ADV_NONCONN_IND;
    switch (param->adv_type)
    {
    case MS_HAL_BLE_ADV_TPYE_IND:
        adv_params.filter = BTMG_LE_PROCESS_ALL_REQ;
        break;
    case MS_HAL_BLE_ADV_TPYE_DIRECT_IND:
        adv_params.filter = BTMG_LE_PROCESS_CONN_REQ;
        break;
    case MS_HAL_BLE_ADV_TPYE_NONCONN_IND:
        break;
    case MS_HAL_BLE_ADV_TPYE_SCAN_IND:
        adv_params.filter = BTMG_LE_PROCESS_SCAN_REQ;
        break;
    case MS_HAL_BLE_ADV_TPYE_EXT_IND:
        break;
    case MS_HAL_BLE_AUX_TPYE_ADV_IND:
        break;
    case MS_HAL_BLE_AUX_TPYE_SYNC_IND:
        break;
    case MS_HAL_BLE_AUX_TPYE_CHAIN_IND:
        break;
    }
    adv_params.own_addr_type = BTMG_LE_RANDOM_ADDRESS;
    adv_params.chan_map =
        BTMG_LE_ADV_CHANNEL_ALL; /*0x07, *bit0:channel 37, bit1:
                                  channel 38, bit2: channel39*/
    bt_manager_le_set_adv_param(&adv_params);
    bt_manager_le_set_random_address();
    if (adv_data->adv_len > 0 && adv_data->adv_data)
    {
        bzero(&nonconn_le_adv_data, sizeof(nonconn_le_adv_data));
        memcpy(nonconn_le_adv_data.data, adv_data->adv_data, adv_data->adv_len);
        nonconn_le_adv_data.data_len = adv_data->adv_len;
        bt_manager_le_set_adv_data(&nonconn_le_adv_data);
    }
    if (adv_data->rsp_len > 0 && adv_data->rsp_data)
    {
        bzero(&nonconn_rsp_data, sizeof(nonconn_rsp_data));
        memcpy(nonconn_rsp_data.data, adv_data->rsp_data, adv_data->rsp_len);
        nonconn_rsp_data.data_len = adv_data->rsp_len;
        bt_manager_le_set_scan_rsp_data(&nonconn_rsp_data);
    }
    if (enable)
    {
        if (bt_manager_le_enable_adv(true) == 0)
        {
            nonconn_le_adv_status = 1;
        }
        else
        {
            return -1;
        }
    }
    return NONNECT_LE_ADV_HANDLE;
}
